ok, 我們有兩個地方需要開始計時
第一個是遊戲開始的時候,
第二個是 turn 結束的時候。
def init(game) do
Process.send_after(self(), {:times_up, :host, 1, 1}, 3000) # 遊戲開始的時候,開始倒數
Process.send_after(self(), {:times_up, :guest, 1, 1}, 3000) # 一人一個
{:ok, game}
end
Process.send_after/3
的第一個變數是收 pid,
我們可以用 self()
來得到目前執行這個地方本身的 pid。
為了等一下好測試,我們先用 3 秒,而不是原本要的 30
再來是時間到了之後要執行的 handle_info :times_up
執行的時候要做的事情有
我直接按照列表直接做,變超長的,我把項目標示在下方註解裡
def handle_info(
{:times_up, player, round, turn},
%{round: current_round, turn: current_turn} = game
)
when round == current_round and turn == current_turn do
# 這個 guard 用來實現 1.檢查目前的遊戲狀態還是不是同個回合
# 2.隨機抽牌
card =
game
|> Map.get(player)
|> Map.get(:hand)
|> Enum.random()
# 我們暫時在這邊用 IO.puts 來印出訊息,待會在 iex 跑的時候比較知道在跑啥
IO.puts("time's up, play #{card} for #{player}")
# 3.代替玩家出牌
# 這個是直接從原本的出牌方法 handle_cast :play_card 複製過來的
game =
game
|> play_card_for(player, card)
|> end_turn()
|> add_wins()
|> end_round()
|> end_game()
# 4. 出牌後,為新回合執行出牌倒數
# 這邊判斷如果 遊戲還在進行,而且這次出牌有導致 換到新的 turn
if game.status == :start && current_turn != game.turn do
Process.send_after(self(), {:times_up, :host, game.round, game.turn}, 3000)
Process.send_after(self(), {:times_up, :guest, game.round, game.turn}, 3000)
end
{:noreply, game}
end
# 如果玩家有出過牌了,就忽略
def handle_info({:times_up, _player, _round, _turn}, game), do: {:noreply, game}
完成了,使用 GenServer 真的讓這件事情簡單很多
除了上面在幫玩家出牌的時候有印出 幫誰出什麼牌之外
在 add_wins 方法,我也暫時用 IO.puts 顯示誰贏一局,與目前比數
defp add_wins(%{turn: turn, round: round, host: host, guest: guest} = game) when turn > 3 do
range_start = (round - 1) * 3
range = range_start..(range_start + 2)
host_desk = Enum.slice(host.desk, range)
guest_desk = Enum.slice(guest.desk, range)
if apply_reverse(
get_score(host_desk) > get_score(guest_desk),
reverse?(host_desk ++ guest_desk)
) do
IO.puts("? Host win this round, host: #{host.wins + 1}, guest: #{guest.wins}") # 顯示該局結果
assign_to_player(game, :host, :wins, game.host.wins + 1)
else
IO.puts("? Guest win this round, host: #{host.wins}, guest: #{guest.wins + 1}") # 顯示該局結果
assign_to_player(game, :guest, :wins, game.guest.wins + 1)
end
end
還有結束遊戲的地方也加上通知
defp end_game(%{guest: %{wins: 2}} = game) do
IO.puts "? winner is Guest"
Map.replace(game, :status, :guest_win)
end
defp end_game(%{host: %{wins: 2}} = game) do
IO.puts "? winner is Host"
Map.replace(game, :status, :host_win)
end
defp end_game(game), do: game
來跑跑看吧,這次只要打一行,因為我們暫時把出牌時間設定成 3 秒,就好像遊戲自動玩一樣
iex(1)> Game.start
{:ok, #PID<0.112.0>}
time's up, play 3 for host
time's up, play reverse for guest
time's up, play 6 for host
time's up, play 3 for guest
time's up, play reverse for host
time's up, play 3 for guest
? Host win this round, host: 1, guest: 0
time's up, play reverse for host
time's up, play 1 for guest
time's up, play 5 for host
time's up, play reverse for guest
time's up, play 1 for host
time's up, play 5 for guest
? Host win this round, host: 2, guest: 0
? winner is Host
執行動圖